package xyz.xtt.gateway;

import java.net.URI;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Objects;

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.core.env.Environment;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.PathMatcher;
import org.springframework.web.server.ServerWebExchange;

import lombok.extern.slf4j.Slf4j;
import reactor.core.publisher.Mono;
import xyz.xtt.gateway.cons.Cons;
import xyz.xtt.gateway.third.ThirdSignInvalidException;
import xyz.xtt.gateway.third.ThirdTokenCheck;
import xyz.xtt.gateway.third.ThirdTokenExpirationException;
import xyz.xtt.gateway.third.ThirdTokenInvalidException;
import xyz.xtt.gateway.third.ThirdTokenLimitedException;
import xyz.xtt.gateway.utils.AESUtil;

/**
 * 鉴名认证
 * 
 * @author huqibo
 *
 */
@RefreshScope
@Slf4j
@Component
public class ThirdAuthGatewayFilter extends AbstractGatewayFilterFactory<ThirdAuthGatewayFilter.Config> {

	@Value("${face.verify.aesKey:RY3rwascwU8cARmX3VsTTW8f}")
	private String verifyAesKey;

	private static final String TEST = "test";
	private final Environment environment;
	private static final String CODE_701001 = "{\"code\":701001,\"message\":\"操作太频繁了,先休息一下吧!\"}";
	private static final String CODE_100000 = "{\"code\":100000,\"message\":\"token无效!\"}";
	private static final String CODE_100001 = "{\"code\":100001,\"message\":\"参数错误!\"}";
	private static final String CODE_301005 = "{\"code\":301005,\"message\":\"签名错误!\"}";
	private static final String CODE_101006 = "{\"code\":101006,\"message\":\"token过期!\"}";
	private static final String CODE_401 = "{\"code\":401,\"message\":\"Unauthorized!\"}";
	private static final String USER_ID = "uid";
	private final PathMatcher pathMatcher = new AntPathMatcher();

	@Value("${run.mode.debug:false}")
	private Boolean runModeDebug;

	@Value("${gateway.skipUrl:[]}")
	private String[] skipUrl;

	@Autowired
	private ThirdTokenCheck thirdTokenCheck;
	@Autowired
	private StringRedisTemplate stringRedisTemplate;

	@Autowired
	public ThirdAuthGatewayFilter(Environment environment) {
		super(Config.class);
		this.environment = environment;
	}

	public static class Config {
		// 过滤器的配置参数
	}

	/**
	 * 检查路径是否在白名单中
	 */
	private boolean checkSkipUrl(String requestUrl) {
		return Arrays.asList(skipUrl).stream().anyMatch(pattern -> pathMatcher.match(pattern, requestUrl));
	}

	@Override
	public GatewayFilter apply(Config config) {
		// 在这里创建并返回过滤器实例
		return (exchange, chain) -> {
			ServerHttpRequest request = exchange.getRequest();
			URI requestUrl = request.getURI();
			String path = exchange.getRequest().getPath().value();
			log.info("SignAuthGatewayFilter path: {}, skipUrl:{}", path, Arrays.toString(skipUrl));
			String authFaceSign = request.getQueryParams().getFirst("authFaceSign");
			if (StringUtils.isNotBlank(authFaceSign)) {
				String decodeNo = URLDecoder.decode(authFaceSign, StandardCharsets.UTF_8);
				String orderNo = AESUtil.decrypt(decodeNo, verifyAesKey);
				String val = stringRedisTemplate.opsForValue().get("nft:" + orderNo);
				log.info("authFaceSign:{},decodeNo:{},orderNo:{},val:{}", authFaceSign, decodeNo, orderNo, val);
				if (StringUtils.isBlank(val)) {
					return responseWrite(exchange, CODE_100001);
				}
				String[] suffVal = StringUtils.split(val, ":");
				if (Objects.isNull(suffVal)) {
					log.info("suffVal isNull ");
					return responseWrite(exchange, CODE_100001);
				}
				if (suffVal.length != 2) {
					log.info("suffVal.length != 2");
					return responseWrite(exchange, CODE_100001);
				}
				String valNo = suffVal[0];
				if (!Objects.equals(valNo, orderNo)) {
					log.info("Objects.equals(valNo, orderNo) not equal {},{}", valNo, orderNo);
					return responseWrite(exchange, CODE_100001);
				}
				String uid = suffVal[1];
				mutateNewHeader(exchange, uid);
				return chain.filter(exchange);
			}
			if (checkSkipUrl(path)) {
				log.info("ThirdAuthGatewayFilter checkSkipUrl:{}", path);
				return chain.filter(exchange);
			}
			HttpHeaders headers = request.getHeaders();
			String authorization = headers.getFirst(Cons.AUTHORIZATION);
			String sign = headers.getFirst(Cons.SIGN);
			String version = headers.getFirst(Cons.VERSION);
			String deviceId = headers.getFirst(Cons.DEVICE_ID);
			String loginType = headers.getFirst(Cons.LOGIN_TYPE);
			String appType = headers.getFirst(Cons.APP_TYPE);
			String device = headers.getFirst(Cons.DEVICE);
			if (checkModel(exchange)) {
				log.info("ThirdAuthGatewayFilter url: {}, runMode:{} skip ThirdAuthGatewayFilter", requestUrl, runModeDebug);
				return chain.filter(exchange);
			}
			boolean anyMatchBlank = Arrays.asList(authorization, sign, version, deviceId, loginType, appType, device).stream().anyMatch(StringUtils::isBlank);
			if (anyMatchBlank) {
				log
						.info("ThirdAuthGatewayFilter uri: {}, authorization:{}, sign:{}, version:{}, device-id:{}, login-type:{}, app-type:{}, device:{}", requestUrl, authorization, sign, version, deviceId, loginType, appType, device);
				return responseWrite(exchange, CODE_100001);
			}
			String userId = StringUtils.EMPTY;
			try {
				userId = thirdTokenCheck.newCheck(requestUrl, authorization, sign, version, deviceId, loginType, appType, device, userId);
			} catch (ThirdTokenInvalidException e) {
				log.error("ThirdTokenInvalidException err: {}", e.getMessage(), e);
				return responseWrite(exchange, CODE_100000);
			} catch (ThirdTokenLimitedException e) {
				log.error("ThirdTokenLimitedException err: {}", e.getMessage(), e);
				return responseWrite(exchange, CODE_701001);
			} catch (ThirdSignInvalidException e) {
				log.error("ThirdSignInvalidException err: {}", e.getMessage(), e);
				return responseWrite(exchange, CODE_301005);
			} catch (ThirdTokenExpirationException e) {
				log.error("ThirdTokenExpirationException err: {}", e.getMessage(), e);
				return responseWrite(exchange, CODE_101006);
			}
			if (StringUtils.isBlank(userId)) {
				log.info("ThirdAuthGatewayFilter thirdTokenCheck.check is null or empty");
				return responseWrite(exchange, CODE_401);
			}
			// 透传uid
			mutateNewHeader(exchange, userId);
			return chain.filter(exchange);
		};
	}

	/**
	 * 状态码改成200
	 * 
	 * @param exchange
	 * @param msg
	 * @return
	 */
	private Mono<Void> responseWrite(ServerWebExchange exchange, String msg) {
		return exchange.getResponse().writeWith(Mono.just(response(exchange, msg))).doOnSubscribe(s -> exchange.getResponse().setStatusCode(HttpStatus.OK));
	}

	private boolean checkModel(ServerWebExchange exchange) {
		String uid = exchange.getRequest().getHeaders().getFirst(USER_ID);
		String[] activeProfiles = environment.getActiveProfiles();
		if (activeProfiles.length > 0) {
			String activeProfile = activeProfiles[0];
			if ((TEST.equals(activeProfile) || "develop".equals(activeProfile)) && StringUtils.isNotBlank(uid) && Objects.equals(runModeDebug, Boolean.TRUE)) {
				// 直接透传用户uid
				mutateNewHeader(exchange, uid);
				return true;
			}
		}
		return false;
	}

	private DataBuffer response(ServerWebExchange exchange, String subject) {
		exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
		exchange.getResponse().getHeaders().setContentType(MediaType.APPLICATION_JSON);
		byte[] bytes = subject.getBytes(StandardCharsets.UTF_8);
		// 将数据写入响应
		DataBuffer buffer = exchange.getResponse().bufferFactory().wrap(bytes);
		return buffer;
	}

	private void mutateNewHeader(ServerWebExchange exchange, String subject) {
		ServerHttpRequest newRequest = exchange.getRequest().mutate().header(USER_ID, subject).build();
		exchange.mutate().request(newRequest).build();
	}
}